#pragma once

#include <iostream>
#include <cstdlib>
#include <cstring>
#include <pthread.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <signal.h>
#include "log.hpp"
#include "Task.hpp"
#include "ThreadPool.hpp"


const int defaultsockfd = -1;
const std::string defaultip = "0.0.0.0";
const int backlog = 5;
enum
{
    SocketError = 2,
    BindError,
    ListenError
};
#ifdef __TEST_CODE3__
struct ThreadData
{
public:
    ThreadData(int fd, const std::string &ip, const uint16_t &port, TcpServer *t) : sockfd(fd), clientip(ip), clientport(port), tsvr(t)
    {
    }

public:
    int sockfd;
    std::string clientip;
    uint16_t clientport;
    TcpServer *tsvr;
};
#endif
class TcpServer
{
public:
    TcpServer(const uint16_t port, const std::string ip = defaultip) : listensockfd_(defaultsockfd), port_(port), ip_(ip)
    {
    }
    void InitServer()
    {
        listensockfd_ = socket(AF_INET, SOCK_STREAM, 0);
        if (listensockfd_ < 0)
        {
            log(Fatal, "create listensockfd_,errno:%d,errstring:%s ", errno, strerror(errno));
            exit(SocketError);
        }
        log(Info, "create listensockfd_ success,listensockfd_:%d ", listensockfd_);
        struct sockaddr_in local;
        // bzero(&local,sizeof(local));
        memset(&local, 0, sizeof(local));
        local.sin_family = AF_INET;
        local.sin_port = htons(port_);
        // local.sin_addr.s_addr = inet_addr(ip_.c_str()); //INADDR_ANY
        inet_aton(ip_.c_str(), &(local.sin_addr));
        int retval = bind(listensockfd_, (const struct sockaddr *)&local, (socklen_t)sizeof(local));
        if (retval < 0)
        {
            log(Fatal, "bind error,errno:%d,errstring:%s ", errno, strerror(errno));
            exit(BindError);
        }
        log(Info, "bind success...listensockfd_: %d ", listensockfd_);
        if (listen(listensockfd_, backlog) < 0)
        {
            log(Fatal, "listen error,errno:%d ,errstring:%s ", errno, strerror(errno));
            exit(ListenError);
        }
        log(Info, "listen success...listensockfd_:%d ", listensockfd_);
    }
#ifdef __TEST_CODE3__
    static void *Routine(void *args)
    {
        pthread_detach(pthread_self());
        ThreadData *td = static_cast<ThreadData *>(args);
        td->tsvr->Service(td->sockfd, td->clientip, td->clientport); // 由该线程提供服务
        delete td;
        return nullptr;
    }
#endif
    void Start()
    {
        signal(SIGPIPE,SIG_IGN);
        ThreadPool<Task>::GetInstance()->Start();
        log(Info, "tcpServer is running...");
        while (true)
        {
            // 1.获取新链接
            struct sockaddr_in client;
            socklen_t len = sizeof(client);
            int sockfd = accept(listensockfd_, (struct sockaddr *)&client, &len);
            if (sockfd < 0)
            {
                log(Warning, "accept error,errno:%d ,errstring:%s ", errno, strerror(errno));
                continue;
            }
            uint16_t clientport = ntohs(client.sin_port);
            char clientip[32];
            inet_ntop(AF_INET, &(client.sin_addr), clientip, sizeof(clientip));

            // 2.根据新链接来进行通信
            log(Info, "get a new link...sockfd:%d,client ip:%s,client port:%d ", sockfd, clientip, clientport);
#ifdef __TEST_CODE1__
            // version 1 -- 单进程版
            Service(sockfd, clientip, clientport);
            close(sockfd);
#endif
#ifdef __TEST_CODE2__
            // version 2 -- 多进程版
            pid_t id = fork();
            if (id == 0)
            {
                // child
                close(listensockfd_);
                if (fork() > 0)
                    exit(0);                           // -- version 1
                Service(sockfd, clientip, clientport); // 利用孙子进程去执行服务与父进程并行，system领养，不需要回收
                close(sockfd);
                exit(0);
            }
            close(sockfd);
            // parent
            pid_t rid = waitpid(id, nullptr, 0); // 阻塞方式回收子进程，满足不了进程并行的要求
            (void)rid;
#endif
            // version 3 --多线程版
#ifdef __TEST_CODE3__
            ThreadData *td = new ThreadData(sockfd, clientip, clientport, this);
            pthread_t tid;
            pthread_create(&tid, nullptr, Routine, td);
#endif
#ifndef __TEST_CODE4__
            // version 4 -- 线程池版
            Task t(sockfd, clientip, clientport);
            ThreadPool<Task>::GetInstance()->Push(t);
#endif
        }
    }
#ifdef __Test_Task__
    void Service(int sockfd, const std::string &clientip, const uint16_t &clientport)
    {
        // test code
        char buffer[4096];
        while (true)
        {
            ssize_t n = read(sockfd, buffer, sizeof(buffer));
            if (n > 0)
            {
                buffer[n] = 0;
                std::cout << "client say# " << buffer << std::endl;
                std::string echo_string = "tcpserver echo# ";
                echo_string += buffer;
                write(sockfd, echo_string.c_str(), echo_string.size());
            }
            else if (n == 0)
            {
                log(Info, "%s:%d quit,server close socket: %d", clientip.c_str(), clientport, sockfd);
                break;
            }
            else
            {
                log(Warning, "read error,sockfd:%d,client ip:%s,client port:%d ", sockfd, clientip.c_str(), clientport);
                break;
            }
        }
    }
#endif
    ~TcpServer() {}

private:
    int listensockfd_;
    uint16_t port_;
    std::string ip_;
};